/*
 * Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "los_time_container.h"
#include "string_util.h"
#include "proc_fs.h"
#include "los_seq_buf.h"


#ifdef LOSCFG_TIME_CONTAINER
extern struct ContainerConfig g_containerConfig;
UINT32 g_currentTimeContainerNum = 1;

TimeContainer g_rootTimeContainer = {};

static TimeContainer *CreateTimeContainer()
{
    TimeContainer *timeContainer = NULL;

    UINT32 size = sizeof(TimeContainer);
    timeContainer = LOS_MemAlloc(m_aucSysMem1, size);

    if (timeContainer == NULL) {
        PRINT_ERR("CreateTimeContainer failed to malloc memory.\n");
        return NULL;
    }

    UINT32 intSave;
    SCHEDULER_LOCK(intSave);
    if (g_currentTimeContainerNum > g_containerConfig.MaxTimeContainer) {
        PRINT_ERR("CreateTimeContainer failed, reach to max container config.\n");
        LOS_MemFree(m_aucSysMem1, timeContainer);
        return NULL;
    }
    g_currentTimeContainerNum += 1;
    SCHEDULER_UNLOCK(intSave);
    atomic_set(&timeContainer->count, 1);
    return timeContainer;
}

static TimeContainer *CreateNewTimeContainer(TimeContainer *runTimeContainer)
{
    TimeContainer *newTimeContainer = NULL;

    newTimeContainer = CreateTimeContainer();
    if (newTimeContainer == NULL) {
        PRINT_ERR("newTimeContainer is NULL.\n");
        return NULL;
    }
    atomic_set(&newTimeContainer->count, 1);
    newTimeContainer->frozenOffsets = false;
    newTimeContainer->offsets.monotonic.tv_nsec = runTimeContainer->offsets.monotonic.tv_nsec;
    newTimeContainer->offsets.monotonic.tv_sec = runTimeContainer->offsets.monotonic.tv_sec;

    ContainerAllocVnum(&(newTimeContainer->containerBase.vnum));

    return newTimeContainer;
}


TimeContainer *HandleTimeContainer(unsigned long flags, TimeContainer *runTimeContainer)
{
    TimeContainer *newTimeContainer = NULL;

    if (!(flags & CLONE_NEWTIME)) {
        if (!runTimeContainer->frozenOffsets) {
            runTimeContainer->frozenOffsets = true;
        }
        (VOID)GetTimeContainer(runTimeContainer);
        return runTimeContainer;
    }

    newTimeContainer = CreateNewTimeContainer(runTimeContainer);
    if (newTimeContainer == NULL) {
        PRINT_ERR("newTimeContainer is NULL.\n");
        return NULL;
    }

    return newTimeContainer;
}

void FreeTimeContainer(TimeContainer *timeContainer)
{
    LOS_MemFree(m_aucSysMem1, timeContainer);

    UINT32 intSave;
    SCHEDULER_LOCK(intSave);
    g_currentTimeContainerNum -= 1;
    SCHEDULER_UNLOCK(intSave);
}

INT32 ReplaceTimeContainerInBundle(ContainerBundle* bundle, struct TimeContainer *targetTimeContainer)
{
    if (!targetTimeContainer->frozenOffsets) {
        targetTimeContainer->frozenOffsets = true;
    }
    DerefTimeContainer(bundle->timeContainer);
    bundle->timeContainer = targetTimeContainer;
    (VOID)GetTimeContainer(bundle->timeContainer);

    DerefTimeContainer(bundle->timeContainerForChildren);
    bundle->timeContainerForChildren = targetTimeContainer;
    (VOID)GetTimeContainer(bundle->timeContainerForChildren);

    return LOS_OK;
}

void InitTimeContainer(TimeContainer *timeContainer)
{
    atomic_set(&timeContainer->count, 2);
    timeContainer->frozenOffsets = true;
    timeContainer->offsets.monotonic.tv_nsec = 0;
    timeContainer->offsets.monotonic.tv_sec = 0;
    ContainerAllocVnum(&(timeContainer->containerBase.vnum));
    return;
}

static int ProcTimeOffsetsOpen(struct Vnode *vnode, struct ProcFile *pf)
{
    TimeContainer *timeContainer = NULL;
    LosProcessCB *process = NULL;
    unsigned int intSave;
    int ret = -EINVAL;
    UINT32 pid;

    SCHEDULER_LOCK(intSave);
    struct ProcDirEntry *pn = (struct ProcDirEntry *)vnode;
    pid = SimpleStrtoll(pn->parent->name, NULL, 10);

    if (OsProcessIDUserCheckInvalid(pid)) {
        SCHEDULER_UNLOCK(intSave);
        return ret;
    }

    process = OS_PCB_FROM_PID(pid);
    if (process != NULL) {
        timeContainer = GetTimeContainer(process->containerBundle->timeContainerForChildren);
        if (timeContainer == NULL) {
            SCHEDULER_UNLOCK(intSave);
            return ret;
        }
    }
    SCHEDULER_UNLOCK(intSave);

    pf->sbuf->private = timeContainer;
    return 0;
}

static int ProcTimeOffsetsRelease(struct Vnode *vnode, struct ProcFile *pf)
{
    struct SeqBuf *seq = pf->sbuf;
    TimeContainer *timeContainer = seq->private;
    DerefTimeContainer(timeContainer);
    return 0;
}

static int ProcTimeOffsetsRead(struct SeqBuf *seqBuf, void *v)
{
    int ret = 0;
    TimeContainer *timeContainer = seqBuf->private;

    if (timeContainer != NULL) {
        ret = LosBufPrintf(seqBuf, "monotonic %lld %ld\n", timeContainer->offsets.monotonic.tv_sec, timeContainer->offsets.monotonic.tv_nsec);
    }

    return ret;
}

static int ProcSetTimensOffset(char *buf, TimeContainer *timeContainer)
{
    int ret = strlen(buf);
    struct timespec64 offsets;
    unsigned int intSave;

    if (buf) {
        *buf = '\0';
        buf++;
        if (*buf == '\0') {
            buf = NULL;
        }
    }

    buf = SkipSpaces(buf);
    offsets.tv_sec = SimpleStrtoll(buf, &buf, 10);

    buf = SkipSpaces(buf);
    offsets.tv_nsec = SimpleStrtoll(buf, &buf, 10);

    SCHEDULER_LOCK(intSave);
    if (timeContainer->frozenOffsets) {
		ret = -EACCES;
		PRINT_ERR("ProcSetTimensOffset frozenOffsets err\n");
		SCHEDULER_UNLOCK(intSave);
		return ret;
	}
    if(offsets.tv_nsec >= OS_SYS_NS_PER_SECOND) {
        ret = -EACCES;
		PRINT_ERR("ProcSetTimensOffset tv_nsec Greater than 1000000000 err\n");
		SCHEDULER_UNLOCK(intSave);
		return ret;
    }

    timeContainer->offsets.monotonic.tv_sec = offsets.tv_sec;
    timeContainer->offsets.monotonic.tv_nsec = offsets.tv_nsec;

    SCHEDULER_UNLOCK(intSave);

    return ret;

}

ssize_t ProcTimeOffsetsWrite(struct ProcFile *file, const char *buf, size_t size, loff_t *ppos)
{
    const char *MONOTINOC = "monotonic";
    struct SeqBuf *seq = file->sbuf;
    TimeContainer *timeContainer = seq->private;
    int ret = -EINVAL;
    char *kbuf = NULL;

    kbuf = MemdupUserNul(buf, size);
    if (kbuf == NULL) {
        return -EINVAL;
    }

    ret = strncmp(kbuf, MONOTINOC, strlen(MONOTINOC));
    if (ret != LOS_OK) {
        ret = -EINVAL;
        return ret;
    }

    ret = ProcSetTimensOffset(kbuf, timeContainer);
    if (ret != LOS_OK) {
        return ret;
    }

    return ret;
}


struct ProcFileOperations g_procTimeOffsetsOperations = {
    .open       = ProcTimeOffsetsOpen,
    .write      = ProcTimeOffsetsWrite,
    .read       = ProcTimeOffsetsRead,
    .release    = ProcTimeOffsetsRelease,
};

struct timespec64 GetOffsetsFromContainer(VOID)
{
    LosProcessCB *runProcessCB = OsCurrProcessGet();
    struct timespec64 timeOffsets;

    timeOffsets.tv_sec = runProcessCB->containerBundle->timeContainer->offsets.monotonic.tv_sec;
    timeOffsets.tv_nsec = runProcessCB->containerBundle->timeContainer->offsets.monotonic.tv_nsec;

    return timeOffsets;
}
#endif

